package com.yanghui.lingyue.functions.ktBase.qmLogic

import java.util.*
import kotlin.math.pow

class LogicExpression(val expression: String) {

    val varNameList = LinkedList<Char>()
    val signStack = LinkedList<LogicNode>()
    val varStack = LinkedList<LogicNode>()
    val expressionQueue = LinkedList<LogicNode>()
    val smallestIDList = LinkedList<Long>()
    val varNum: Int
        get() = varNameList.size

    val smallestItemList = LinkedList<OriginSmallestItem>()

    init {
        val signArray = arrayOf('*', '+', '\'', '(', ')', '&', '|')
        val buffer = LinkedList<Char>()
        val ex = expression.toList()
        for (i in 0 until ex.size - 1) {
            buffer.add(ex[i])
            if (ex[i] == '\'' && ex[i + 1].isLetter()) {
                buffer.add('*')
            } else if (ex[i] == ')' && ex[i + 1] == '(') {
                buffer.add('*')
            } else if (ex[i].isLetter() && ex[i + 1].isLetter()) {
                buffer.add('*')
            } else if (ex[i] == '\'' && ex[i + 1] == '(') {
                buffer.add('*')
            }
        }
        buffer.add(ex.last())

        while (buffer.isNotEmpty()) {
            val c = buffer.removeFirst()
            if (c in signArray) {
                when (c) {
                    '*', '&' -> {
                        val node = LogicNode(LogicNode.Type.SIGN)
                        node.sign = LogicNode.SIGNType.AND
                        while (signStack.isNotEmpty()) {
                            if (signStack[0].sign.ordinal >= node.sign.ordinal && signStack[0].sign != LogicNode.SIGNType.LEFT) {
                                expressionQueue.add(signStack.removeFirst())
                            } else {
                                break
                            }
                        }
                        signStack.addFirst(node)
                    }
                    '+', '|' -> {
                        val node = LogicNode(LogicNode.Type.SIGN)
                        node.sign = LogicNode.SIGNType.OR
                        while (signStack.isNotEmpty()) {
                            if (signStack[0].sign.ordinal >= node.sign.ordinal && signStack[0].sign != LogicNode.SIGNType.LEFT) {
                                expressionQueue.add(signStack.removeFirst())
                            } else {
                                break
                            }
                        }
                        signStack.addFirst(node)
                    }
                    '\'' -> {
                        val node = LogicNode(LogicNode.Type.SIGN)
                        node.sign = LogicNode.SIGNType.NOT
                        while (signStack.isNotEmpty()) {
                            if (signStack[0].sign.ordinal >= node.sign.ordinal && signStack[0].sign != LogicNode.SIGNType.LEFT) {
                                expressionQueue.add(signStack.removeFirst())
                            } else {
                                break
                            }
                        }
                        signStack.addFirst(node)
                    }
                    '(' -> {
                        val node = LogicNode(LogicNode.Type.SIGN)
                        node.sign = LogicNode.SIGNType.LEFT
                        signStack.addFirst(node)
                    }
                    ')' -> {
                        while (signStack.isNotEmpty()) {
                            if (signStack[0].sign != LogicNode.SIGNType.LEFT)
                                expressionQueue.add(signStack.removeFirst())
                            else {
                                signStack.removeFirst()
                                break
                            }
                        }
                    }
                }
            } else {
                val node = LogicNode(LogicNode.Type.VAR)
                node.varName = c
                if (c !in varNameList) {
                    varNameList.add(c)
                }
                expressionQueue.add(node)
            }
        }
        while(signStack.isNotEmpty()) {
            expressionQueue.add(signStack.removeFirst())
        }

        if (varNameList.size > 14) {
            throw Exception()
        }

        for (sid in 0L until 2.0.pow(varNameList.size.toDouble()).toLong()) {
            val numStack = LinkedList<Boolean>()
            expressionQueue.forEach {
                if (it.type == LogicNode.Type.VAR) {
                    numStack.addFirst((sid ushr varNameList.indexOf(it.varName)) and 1 == 1L)
                } else {
                    when(it.sign) {
                        LogicNode.SIGNType.NOT ->{
                            numStack[0] = !numStack[0]
                        }
                        LogicNode.SIGNType.AND ->{
                            numStack.addFirst(
                                numStack.removeFirst() and numStack.removeFirst()
                            )
                        }
                        LogicNode.SIGNType.OR ->{
                            numStack.addFirst(
                                numStack.removeFirst() or numStack.removeFirst()
                            )
                        }
                    }
                }
            }
            if (numStack[0]) {
                addSmallestItem(sid)
                smallestIDList.add(sid)
            }
        }

    }

    private fun addSmallestItem(initNum: Long) {
        smallestItemList.add(OriginSmallestItem(varNum, initNum))
    }
}

class LogicNode(val type: Type) {
    enum class Type {
        SIGN, VAR
    }

    enum class SIGNType : Comparable<SIGNType> {
        OR, AND, NOT, LEFT, RIGHT
    }

    var varName: Char = ' '
    var sign: SIGNType = SIGNType.OR

    override fun toString(): String {
        return if (type == Type.SIGN)
            when(sign) {
                SIGNType.OR ->"OR"
                SIGNType.AND ->"AND"
                SIGNType.NOT ->"NOT"
                SIGNType.LEFT ->"LEFT"
                SIGNType.RIGHT ->"RIGHT"
            }
        else
            "$varName"
    }
}